package com.xframework.security.util;

import com.xframework.exception.BusinessException;
import com.xframework.redis.XRedisTemplate;
import com.xframework.security.model.SecurityAccount;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JWTUtil {
    private static volatile Key signingKey;
    private static String TYP = "typ";
    private static String JWT = "JWT";
    private static String ALG = "alg";
    private static String RS256 = "RS256";

    @Autowired
    private static XRedisTemplate xRedisTemplate;

//    public Date getExpirationDateFromToken(String jwt) {
//        Date expiration;
//        try {
//            Claims claims = parserJWT(jwt);
//            expiration = claims.getExpiration();
//        } catch (Exception e) {
//            expiration = null;
//        }
//        return expiration;
//    }

//    private Boolean isTokenExpired(String token) {
//        final Date expiration = getExpirationDateFromToken(token);
//        return expiration.before(new Date());
//    }


    //使用HS256签名算法和生成的signingKey最终的Token,claims中是有效载荷
    public static String createJWT(SecurityAccount securityAccount) {
        String accountId = securityAccount.getAccountId();
        Date expirationDate = generateExpirationDate(Constant.securityJwtExpiration);
        String expiration = expirationDate.toString();
        xRedisTemplate.setString(Constant.SECURITY_EXPIRATION_ + accountId, expiration, Constant.securityJwtExpiration);
        return Jwts.builder()
                .setHeaderParam(TYP, JWT)
                .setHeaderParam(ALG, RS256)
                .setId(securityAccount.getAccountId())
                .setAudience(securityAccount.getAccountName())
//                .setClaims(map)
                .setSubject(securityAccount.getApplicationId())
                .setIssuer(Constant.SECURITY_ACCOUNT)
                .setIssuedAt(Calendar.getInstance().getTime())
//                .setNotBefore()
                .setExpiration(expirationDate)
                .compressWith(CompressionCodecs.DEFLATE)
//                .signWith(SignatureAlgorithm.HS256, getKeyInstance())
                .signWith(SignatureAlgorithm.RS256, RSAUtil.getPrivateKey())
                .compact();
    }

    //解析Token，同时也能验证Token，当验证失败返回null
    public static Claims parserJWT(String jwt) {
        Claims claims = null;
        try {
//            claims = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwt).getBody();
            claims = Jwts.parser().setSigningKey(RSAUtil.getPublicKey()).parseClaimsJws(jwt).getBody();
            String accountId = claims.getId();
            String expiration = claims.getExpiration().toString();
            if (Constant.securityJwtDelay && !expiration.equals(xRedisTemplate.get(Constant.SECURITY_EXPIRATION_ + accountId))) {
                claims = null;
            }
        } catch (Exception e) {
            claims = null;
            throw new BusinessException(Constant.securityExpirationMessage, e);
        } finally {
            return claims;
        }
    }

    //该方法使用HS256算法和Secret:bankgl生成signKey
    private synchronized static Key getKeyInstance() {
        if (signingKey == null) {
            //We will sign our JavaWebToken with our ApiKey secret
            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
            byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(Constant.securityJwtKey);
            signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        }
        return signingKey;
    }

    private static Date generateExpirationDate(long expiration) {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    public static void main(String[] args) {
        Map<String, Object> map = new HashMap<String, Object>();
//        map.put("id", 1);
//        map.put("name", "xuning");
//        SecurityAccount securityAccount = new SecurityAccount();
//        securityAccount.setApplicationId("sub setApplicationId xxxxxxxxxxxxx");
//        securityAccount.setAccountId("jti setAccountId rrrrrrrrrrrrrrrr");
//        securityAccount.setAccountName("aud setAccountName xuningx");
//        String token = createJWT(securityAccount);
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsInppcCI6IkRFRiJ9.eNqqVsoqyVSyUjJU0lFKLE0BshJTcjPzgLzi0iQgzwA_AGnLLC4GKgx2dQ4N8gyJjHd0dvYP9QsBSSSWKFkZmhkamZubmpka6yilVhTABMwsTI1rAQAAAP__.sSVeEHRtZTN7427w5RqOQVjSpP1tlKeW8Wuuvrq0eOEzFBn-PIzFK0QB2pbXbNEBbqSpJZ3OVU-KGt3zAGDl_7Dp9k1fYkFhGMLk3KqNZKJiooWyGg0bgsZVJol_pauUqqvgGy5G8uVl87kq2uksjZAr8ipCo8hQOIMtsMPk9F_WOD5PlBLRKcyKvavO8bgqOkEyZwImGVe3h9bjiKGn2LSAq8YHAnDl6j2FidJIpMoQ5vgJqlmjGwhchGHP2MkdaywLNrTt4z0OWCi9SqMNwhHn-SLwQ3ycZpqGQ3bsfHj9mx8Au9LixU0FbQoQUDqN-cQ1XV7H7Vf1-69oL5DUhA";
        map = parserJWT(token);
        for (String key : map.keySet()) {
            System.out.println(key + "==========" + map.get(key));
        }
        System.out.println(map == null);
        System.out.println(map.get("aud"));
        System.out.println(map.get("jti"));
        System.out.println(map.get("sub"));

    }
}